package com.grt.condify.common;


import com.google.gson.Gson;
import com.grt.condify.common.converter.IConverter;
import com.grt.condify.utils.StringUtil;
import springfox.documentation.spi.service.contexts.Defaults;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.grt.condify.utils.PreConditionCheck.checkNotNull;
import static com.grt.condify.utils.StringUtil.showEmpty;

/**
 * 报告类的实用外观器。该类封装了可能包含的报告场景，包含：成功、失败两种情况。
 * 用户能够初始一次该外观器而添加任意个成功或失败的报告并能够按照需要产生针对成功、失败、综合的报告
 * 报告形式为String或者Json格式的字符串
 * 如果默认产生的格式不能够满足用户需求，用户可以自行重现对应的报告接口
 * <p>
 * Created by cy on 2015/11/25.
 */
public class Reports {

    private final String title;
    private final List<Report> errorReports = new ArrayList<>();
    private final List<Report> unknownReports = new ArrayList<>();
    private final List<Report> successReports = new ArrayList<>();

    /**
     * 报告携带的上下文
     */
    private Object context;

    public Object getContext() {
        return context;
    }

    public void setContext(Object context) {
        this.context = context;
    }

    private Reports(String title) {
        this(title, null);
    }

    private Reports(String title, Object context) {
        this.title = showEmpty(title);
        this.context = context;
    }

    public String getTitle() {
        return title;
    }

    public List<Report> getErrorReports() {
        return errorReports;
    }

    public List<Report> getSuccessReports() {
        return successReports;
    }

    public List<Report> getUnknownReports() {
        return unknownReports;
    }

    /**
     * 创建一次报告
     *
     * @param title 本次报告的名字，报告名字不能够为空
     * @return 本次报告实例
     */
    public static Reports createNewReports(String title) {
        return createNewReports(title, null);
    }

    public static Reports createNewReports(String title, Object context) {
        return new Reports(checkNotNull(title, "the report must be named !"), context);
    }

    public void addReport(Report report) {
        if (report != null) {
            switch (report.getResult()) {
                case SUCCESS:
                    successReports.add(report);
                    break;
                case FAIL:
                    errorReports.add(report);
                    break;
                case UNKNOW:
                default:
                    unknownReports.add(report);
                    break;
            }
        }
    }

    /**
     * 添加错误信息
     *
     * @param content 错误信息
     */
    public void error(String content) {
        error(content, null);
    }

    public void error(String content, Object context) {
        addReport(new Report<>(this.title, content, Report.RESULT.FAIL, context));
    }


    /**
     * 添加错误信息，信息来源是一个异常
     * 我们会尝试取得异常的错误信息和其导致其错误的信息并将此拼接成字符串进行处理
     *
     * @param exception 错误信息
     */
    public void error(Throwable exception) {
        error(exception, -1, null);
    }

    public void error(Throwable exception, Object context) {
        error(exception, -1, context);
    }

    /**
     * 添加错误信息，信息来源是一个异常
     * 我们会尝试取得异常的错误信息和其导致其错误的信息并将此拼接成字符串进行处理
     *
     * @param exception       错误信息
     * @param stackInfoNumber 打印堆栈的条数
     */
    public void error(Throwable exception, int stackInfoNumber) {
        error(exception, stackInfoNumber, null);
    }

    public void error(Throwable exception, int stackInfoNumber, Object context) {
        Throwable content = exception;
        if (exception == null) {
            content = unknowError();
        }
        String errorMessage = content.toString();
        //cause content
        String causeMessage = content.getCause() == null ? " none " : content.getCause().getMessage();
        String errorInfo = "fail information is : " + errorMessage +
                ", cause is : " + causeMessage +
                ", stack trace is : " + getStackTraceInfos(content, stackInfoNumber);
        //regist report as String
        error(errorInfo, context);
    }

    private Throwable unknowError() {
        return new Throwable("i don't know where is wrong", new Throwable("u didn't tell me"));
    }

    private String getStackTraceInfos(Throwable exception, int length) {
        StringBuilder stackTraces = new StringBuilder();
        StackTraceElement[] stacks = exception.getStackTrace();
        int maxNumberOfStackInfo = Math.min(stacks.length, length);

        for (int count = 0; count < (maxNumberOfStackInfo < 0 ? stacks.length : maxNumberOfStackInfo); count++) {
            String content = checkNotNull(stacks[count]).toString();
            content = StringUtil.isEmpty(content) ? StringUtil.showEmpty(content) : content;
            stackTraces.append(content).append(" ; ");
        }
        return stackTraces.toString();
    }

    /**
     * 添加错误信息，错误信息可以是任何的对象
     *
     * @param content 错误信息对象
     * @param <C>     可以是任何对象
     */
    public <C> void error(C content) {
        error(content, null);
    }

    public <C> void error(C content, Object context) {
        addReport(new Report<>(this.title, content, Report.RESULT.FAIL, context));
    }

    /**
     * 创建成功的报告
     *
     * @param content 成功报告内容
     */
    public void success(String content) {
        success(content, null);
    }

    public void success(String content, Object context) {
        addReport(new Report<>(this.title, showEmpty(content), Report.RESULT.SUCCESS, context));
    }

    /**
     * 创建成功报告，成功信息可以是任何对象
     *
     * @param content 成功的信息对象
     * @param <C>     可以是任何对象
     */
    public <C> void success(C content) {
        success(content, null);
    }

    public <C> void success(C content, Object context) {
        addReport(new Report<>(this.title, content, Report.RESULT.SUCCESS, context));
    }

    /**
     * 获得成功信息，其实拼接内容也是类Json
     * 格式为："result":"success","number“:%s[,"content":%s]
     *
     */
    public String reportSuccessInfoAsString(boolean needDetail) {
        if (successReports.size() == 0) {
            return "";
        }
        String sizeOfReports = String.valueOf(successReports.size());
        StringBuilder sReports = new StringBuilder();
        sReports.append("\"result\":\"success\"").append(",").append("\"number\":\"").append(sizeOfReports).append("\"");
        if (needDetail) {
            sReports.append(String.format(",\"content\":%s", getDetailAsJson(successReports)));
        }
        return sReports.toString();
    }

    /**
     * 获得失败的信息，和获得失败信息格式类似
     * 格式为：“result”:”fail”,
     * “error_info”:[
     * {“docid”:”xxxxx”,”info”:”xxxx”},
     * {“docid”:”xxxxa”,”info”:”xxxx”},
     * ……
     * ]
     *
     */
    public String reportFailInfoAsString() {

        if (errorReports.size() == 0) {
            return "";
        }
        return "\"result\":\"fail\"" + ",\"error_info\":" + getDetailAsJson(errorReports);
    }

    /**
     * 将报告已json形式进行返回，格式上和与字符串返回类似，
     * 格式如下：{"result":"success","number“:%s[,"content":%s]}
     *
     * @param needDetail 如果为true，则有content内容
     */
    public String reportSuccessInfoAsJson(boolean needDetail) {
        return String.format("{%s}", reportSuccessInfoAsString(needDetail));
    }

    /**
     * 获得失败的信息，和获得失败信息格式类似
     * 格式为：{“result”:”fail”,
     * “error_info”:[
     * {“docid”:”xxxxx”,”info”:”xxxx”},
     * {“docid”:”xxxxa”,”info”:”xxxx”},
     * ……
     * ]}
     *
     */
    public String reportFailInfoAsJson() {
        return String.format("{%s}", reportFailInfoAsString());
    }

    /**
     * format : [{one report},{one report},...]
     *
     * @param details reports
     */
    protected String getDetailAsJson(List<Report> details) {
        if (details == null || details.size() == 0) {
            return "";
        }
        StringBuilder finalDetails = new StringBuilder("[");  //begin with [
        for (Report report : details) {
            Object content = report.getDetail();
            String reportContent = content == null ? "" : new Gson().toJson(content);
            if (finalDetails.length() > 1) {
                finalDetails.append(",");   //seperate by ','
            }
            finalDetails.append(reportContent);
        }
        finalDetails.append("]");
        return finalDetails.toString();
    }

    /**
     * Reports协助类,帮助将一些对象翻译成便于报告的String
     */
    public static class ObjectToStringConverter implements IConverter<Object, String> {
        @Override
        public String convert(Object value) {
            if (value == null) {
                return "None";
            } else if (value instanceof Map) {
                return "Map whose size is " + ((Map) value).size();
            } else if (value instanceof Collection) {
                return "Collection whose size is " + ((Collection) value).size();
            } else {
                return value.toString();
            }
        }
    }
}


